#include "DXEngine.h"
#include <dxerr.h>



CDXEngine::CDXEngine()
{
	m_hWnd = NULL;
	m_pD3D = NULL;
	m_pD3DDevice = NULL;
	m_hInst = (HINSTANCE)GetModuleHandle(NULL);
	ZeroMemory( &m_D3DPresentParams, sizeof(D3DPRESENT_PARAMETERS) );
	m_deviceType = D3DDEVTYPE_REF;
	m_deviceBehavior = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
	m_uiWinWidth = 0;
	m_uiWinHeight = 0;
	m_bWindowed = false;
}

CDXEngine::~CDXEngine()
{
	shutdown();
}


void CDXEngine::shutdown()
{
	m_pInput.reset();
	m_pTimer.reset();
	ZeroMemory( &m_D3DPresentParams, sizeof(D3DPRESENT_PARAMETERS) );
	ReleaseCOM(m_pD3DDevice);
	ReleaseCOM(m_pD3D);
}

CDXEngine* CDXEngine::getInstance()
{
	static CDXEngine pInstance;

	return &pInstance;
}


bool CDXEngine::update() 
{
	MSG		msg;

	if ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
	{
		if (msg.message == WM_QUIT)
			return false;
		TranslateMessage( &msg );
		DispatchMessage ( &msg );
	}
	else
	{
		m_pInput->Poll();
		if (m_pInput->KeyDown(DIK_ESCAPE) )
			return false;
//		scene->update();
	}
	return true;
}

bool CDXEngine::render(SceneGraphPtr scene) 
{
	if (m_pTimer->Tick())
	{
		HR(m_pD3DDevice->Clear(0, 0,
			D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
			D3DCOLOR_XRGB(0,0,250),
			1.0f, 0));
		HR(m_pD3DDevice->BeginScene());
		
		scene->render();

		HR(m_pD3DDevice->EndScene());
		m_pD3DDevice->Present(NULL, NULL, NULL, NULL);
	}
	return true;
}


bool CDXEngine::init(LPCSTR strWinTitle,
			unsigned int uiWinWidth,
			unsigned int uiWinHeight,
			bool bWindowed)
{
	m_uiWinWidth = uiWinWidth;
	m_uiWinHeight = uiWinHeight;
	m_bWindowed = bWindowed;
	if (initDisplay(strWinTitle) == false)
		return false;
//	initFont();
	m_pInput.reset(new CDXEngineInput());
	m_pInput->Init(m_hInst, m_hWnd,DISCL_NONEXCLUSIVE|DISCL_FOREGROUND,
					DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);
	m_pTimer.reset(new CDXEngineTimer());
	return true;
}




bool CDXEngine::initDisplay(LPCSTR strWinTitle)
{
	if (localRegisterClass(strWinTitle) == false)
		return false;
	m_hWnd = CreateWindow(strWinTitle,
						  strWinTitle,
						  (WS_OVERLAPPEDWINDOW | WS_SYSMENU) & ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME),
						  CW_USEDEFAULT, CW_USEDEFAULT,
						  m_uiWinWidth, m_uiWinHeight,
						  (HWND)NULL,
						  (HMENU)NULL,
						  m_hInst,
						  NULL );
	if (!m_hWnd)
		return false;
	ShowWindow(m_hWnd, SW_SHOW);
	if (!initDirect3D())
		return false;
	return (checkShader());
}


ATOM CDXEngine::localRegisterClass(LPCSTR strWinTitle)
{
	m_wc.style = CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
	m_wc.lpfnWndProc = &CDXEngine::wndProc;
	m_wc.cbClsExtra = NULL;
	m_wc.cbWndExtra = NULL;
	m_wc.hInstance = m_hInst;
	m_wc.hIcon = NULL;
	m_wc.hCursor = NULL;
	m_wc.hbrBackground =  static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
	m_wc.lpszMenuName = NULL;
	m_wc.lpszClassName = strWinTitle;
	return (RegisterClass(&m_wc));
}


LRESULT CALLBACK CDXEngine::wndProc(HWND hWnd,
                             UINT message, 
                             WPARAM wParam, 
                             LPARAM lParam)
{
	switch(message)
	{
	case WM_CLOSE:
		DestroyWindow(hWnd);
		return 0;
	case WM_DESTROY: 
		PostQuitMessage(0);
		break;
	default:
		return(DefWindowProc(hWnd, message, wParam, lParam));
	}
	return DefWindowProc( hWnd, message, wParam, lParam );
}


bool CDXEngine::initDirect3D()
{
	m_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
	if (m_pD3D == NULL)
		return false;
	m_D3DPresentParams.Windowed = m_bWindowed;
	m_D3DPresentParams.BackBufferCount = 1;
	m_D3DPresentParams.BackBufferFormat = findBackBufferFormat( D3DADAPTER_DEFAULT,
																D3DDEVTYPE_HAL,
																m_D3DPresentParams.Windowed );
	if (!m_D3DPresentParams.Windowed)
	{
		m_D3DPresentParams.BackBufferWidth = m_uiWinWidth;
		m_D3DPresentParams.BackBufferHeight = m_uiWinHeight;
	}
	else
	{
		m_D3DPresentParams.BackBufferWidth = 0;
		m_D3DPresentParams.BackBufferHeight = 0;
	}
	m_D3DPresentParams.MultiSampleType = D3DMULTISAMPLE_NONE;
	m_D3DPresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
	m_D3DPresentParams.hDeviceWindow = m_hWnd;
	m_D3DPresentParams.EnableAutoDepthStencil = true;
	m_D3DPresentParams.AutoDepthStencilFormat = findDepthStencilFormat( D3DADAPTER_DEFAULT,
																		m_D3DPresentParams.BackBufferFormat,
																		D3DDEVTYPE_HAL );
	m_D3DPresentParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
	m_D3DPresentParams.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	if (!createDev())
		return false;
	return true;
}



D3DFORMAT CDXEngine::findDepthStencilFormat(ULONG adapterOrdinal,
											D3DFORMAT Format,
											D3DDEVTYPE devType)
{
	if (SUCCEEDED(m_pD3D->CheckDeviceFormat(adapterOrdinal,
											devType,
											Format,
											D3DUSAGE_DEPTHSTENCIL,
											D3DRTYPE_SURFACE,
											D3DFMT_D24S8)))
	{
		return D3DFMT_D24S8;
	}
	if (SUCCEEDED(m_pD3D->CheckDeviceFormat(adapterOrdinal,
											devType,
											Format,
											D3DUSAGE_DEPTHSTENCIL,
											D3DRTYPE_SURFACE,
											D3DFMT_D15S1)))
	{
		return D3DFMT_D15S1;
	}
	return D3DFMT_UNKNOWN;
}




D3DFORMAT CDXEngine::findBackBufferFormat(ULONG adapterOrdinal,
											D3DDEVTYPE devType,
											BOOL bWindowed)
{
	if (SUCCEEDED(m_pD3D->CheckDeviceType(adapterOrdinal,
											devType,
											D3DFMT_X8R8G8B8,
											D3DFMT_X8R8G8B8,
											bWindowed)))
	{
		return D3DFMT_X8R8G8B8;
	}
	if (SUCCEEDED(m_pD3D->CheckDeviceType(adapterOrdinal,
											devType,
											D3DFMT_X1R5G5B5,
											D3DFMT_X1R5G5B5,
											bWindowed)))
	{
		return D3DFMT_X1R5G5B5;
	}
	if (SUCCEEDED(m_pD3D->CheckDeviceType(adapterOrdinal,
											devType,
											D3DFMT_R5G6B5,
											D3DFMT_R5G6B5,
											bWindowed)))
	{
		return D3DFMT_R5G6B5;
	}
	return D3DFMT_UNKNOWN;
}



bool CDXEngine::createDev(void)
{
	HRESULT			hr = S_OK;

	if( FAILED(hr = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT,
									(m_deviceType = D3DDEVTYPE_HAL),
									m_hWnd,
									(m_deviceBehavior = D3DCREATE_HARDWARE_VERTEXPROCESSING),
									&m_D3DPresentParams,
									&m_pD3DDevice)))
	{
		DXTrace( __FILE__, __LINE__, hr, TEXT("Cannot create DX9 Device with HAL/HW-VertexProcess."), false );
		if( FAILED(hr = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT,
											 (m_deviceType = D3DDEVTYPE_HAL),
											 m_hWnd,
											 (m_deviceBehavior = D3DCREATE_SOFTWARE_VERTEXPROCESSING),
											 &m_D3DPresentParams,
											 &m_pD3DDevice)))
		{
			DXTrace( __FILE__, __LINE__, hr, TEXT("Cannot create DX9 Device with HAL/SW-VertexProcess."), false );
			D3DDISPLAYMODE	CurrentMode;

			m_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &CurrentMode);
			m_D3DPresentParams.AutoDepthStencilFormat = findDepthStencilFormat(	D3DADAPTER_DEFAULT,
																				CurrentMode.Format,
																				D3DDEVTYPE_REF);
			if( FAILED(hr = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT,
												 (m_deviceType = D3DDEVTYPE_REF),
												 m_hWnd,
												 (m_deviceBehavior = D3DCREATE_SOFTWARE_VERTEXPROCESSING),
												 &m_D3DPresentParams,
												 &m_pD3DDevice)))
			{
				DXTrace( __FILE__, __LINE__, hr, TEXT("Cannot create DX9 Device with REF/SW-VertexProcess."), false );
				MessageBoxA(m_hWnd, "Cannot Create DX9 Device.", "Error", (MB_ICONERROR | MB_OK) );
				return false;
			}
		}
	}
	return true;
}


bool CDXEngine::initFont(void)
{
/*	HR(D3DXCreateFont(m_pD3DDevice,
						14,
						0,
						FW_NORMAL,
						1,
						FALSE,
						DEFAULT_CHARSET,
						OUT_DEFAULT_PRECIS,
						ANTIALIASED_QUALITY,
						DEFAULT_PITCH | FF_DONTCARE,
						TEXT("Arial"),
						&m_pFont ));*/
	return true;
}



bool CDXEngine::checkShader(void)
{
	D3DCAPS9 caps;

	HR(m_pD3DDevice->GetDeviceCaps(&caps));
	if (caps.VertexShaderVersion < D3DVS_VERSION(3, 0))
		return false;
	if (caps.PixelShaderVersion < D3DPS_VERSION(3, 0))
		return false;
	return true;
}


void CDXEngine::resetDevice()
{
	HR(m_pD3DDevice->Reset(&m_D3DPresentParams));
}

